﻿using System;
using System.Collections.ObjectModel;

using Silverlight.SqlStatementEvaluator;
using System.Globalization;
using System.Data;

namespace Silverlight
{
    /// <summary>
    /// Sorts DataView
    /// </summary>
    public static class Sorter
    {
        /// <summary>
        /// Orders DataRows according to ORDER BY closure
        /// </summary>
        /// <param name="rows">DataRow collection</param>
        /// <param name="orderByClosure">ORDER BY closure</param>
        public static void OrderBy(DataRowCollection rows, string orderByClosure)
        {
            // Do nothing if rows count is less than 2
            if (rows.Count < 2)
            {
                return;
            }

            // Analizing SQL statement, split it into string parts
            StringAnalizer stringAnalizer = new StringAnalizer(orderByClosure, SqlStatementPartEnum.OrderBy);
            Collection<StringPart> stringParts = stringAnalizer.Analize();

            // Do nothing if sort criteria weren't specified
            if (stringParts.Count == 0)
            {
                return;
            }

            // Pepare temporary DataRow colletion for sorted rows
            DataRowCollection sortedDataRowCollection = new DataRowCollection(false);

            // Sort DataRow collection
            for (int i = rows.Count - 1; i >= 0; i--)
            {
                // Remove DataRow from collection
                DataRow dataRow = rows[i];
                rows.Remove(dataRow);

                // Compare DataRow to all sorted rows
                bool wasAdded = false;
                for (int j = 0; j < sortedDataRowCollection.Count; j++)
                {
                    DataRow sortedDataRow = sortedDataRowCollection[j];

                    // Process sort conditions
                    for (int k = 0; k < stringParts.Count; k++)
                    {
                        StringPart stringPart = stringParts[k];

                        //switch (Sorter.Compare(dataRow[stringPart.FieldName], sortedDataRow[stringPart.FieldName], dataRow.ItemArray[stringPart.FieldName].TypeName, stringPart.Type))
                        //{
                        //    case -1:
                        //        if (stringPart.Type == StringPartType.Asc)
                        //        {
                        //            // DataRow must be inserted before sorted row
                        //            sortedDataRowCollection.Insert(j, dataRow);
                        //            wasAdded = true;
                        //        }
                        //        else
                        //        {
                        //            // DataRow must be inserted after sorted row
                        //            sortedDataRowCollection.Insert(j, dataRow);
                        //            wasAdded = true;
                        //        }

                        //        break;
                        //    case 0:
                        //        // Continue comparison using next StringPart
                        //        continue;
                        //    case 1:
                        //        // Continue comparison using next sorted row
                        //        break;
                        //}

                        break;
                    }

                    if (wasAdded)
                    {
                        break;
                    }
                }

                // Add DataRow if it was not added
                if (!wasAdded)
                {
                    sortedDataRowCollection.Add(dataRow);
                }
            }

            // Move DataRows back to original DataRowCollection
            for (int i = sortedDataRowCollection.Count - 1; i >= 0; i--)
            {
                DataRow dataRow = sortedDataRowCollection[i];
                sortedDataRowCollection.Remove(dataRow);
                rows.Insert(0, dataRow);
            }
        }

        /// <summary>
        /// Compares two values according to their data type
        /// </summary>
        /// <param name="value1">First value</param>
        /// <param name="value2">Second value</param>
        /// <param name="dataTypeName">Data type full name</param>
        /// <param name="stringPartType">String part type</param>
        /// <returns>
        /// -1 if value1 < value2
        /// 0 if value1 = value2
        /// 1 if value1 > value2
        /// </returns>
        private static int Compare(string value1, string value2, string dataTypeName, StringPartType stringPartType)
        {
            // Handle nulls
            bool value1IsNull = false;
            bool value2IsNull = false;

            if (string.IsNullOrEmpty(value1))
            {
                value1IsNull = true;
            }

            if (string.IsNullOrEmpty(value2))
            {
                value2IsNull = true;
            }

            // Both null values are equal
            if (value1IsNull && value2IsNull)
            {
                return 0;
            }

            // Put nulls at the top
            if (value1IsNull)
            {
                if (stringPartType == StringPartType.Asc)
                {
                    return -1;
                }
                else
                {
                    return 1;
                }
            }

            if (value2IsNull)
            {
                if (stringPartType == StringPartType.Asc)
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }

            // At this point both values are not null
            switch (dataTypeName)
            {
                case "Boolean":
                    bool boolValue1 = bool.Parse(value1);
                    bool boolValue2 = bool.Parse(value2);
                    if (boolValue1 == boolValue2)
                    {
                        return 0;
                    }
                    else
                    {
                        if (stringPartType == StringPartType.Asc)
                        {
                            if (boolValue1 == false)
                            {
                                return -1;
                            }
                            else
                            {
                                return 1;
                            }
                        }
                        else
                        {
                            if (boolValue1 == false)
                            {
                                return 1;
                            }
                            else
                            {
                                return -1;
                            }
                        }
                    }
                case "Byte":
                    byte byteValue1 = byte.Parse(value1, CultureInfo.InvariantCulture);
                    byte byteValue2 = byte.Parse(value2, CultureInfo.InvariantCulture);
                    if (byteValue1 == byteValue2)
                    {
                        return 0;
                    }
                    else
                    {
                        if (stringPartType == StringPartType.Asc)
                        {
                            if (byteValue1 == 0)
                            {
                                return -1;
                            }
                            else
                            {
                                return 1;
                            }
                        }
                        else
                        {
                            if (byteValue1 == 0)
                            {
                                return 1;
                            }
                            else
                            {
                                return -1;
                            }
                        }
                    }
                case "DateTime":
                    DateTime dateTimeValue1 = DateTime.Parse(value1, CultureInfo.InvariantCulture);
                    DateTime dateTimeValue2 = DateTime.Parse(value2, CultureInfo.InvariantCulture);
                    if (dateTimeValue1 == dateTimeValue2)
                    {
                        return 0;
                    }
                    else
                    {
                        if (stringPartType == StringPartType.Asc)
                        {
                            if (dateTimeValue1 < dateTimeValue2)
                            {
                                return -1;
                            }
                            else
                            {
                                return 1;
                            }
                        }
                        else
                        {
                            if (dateTimeValue1 < dateTimeValue2)
                            {
                                return 1;
                            }
                            else
                            {
                                return -1;
                            }
                        }
                    }
                case "Decimal":
                    Decimal decimalValue1 = Decimal.Parse(value1, CultureInfo.InvariantCulture);
                    Decimal decimalValue2 = Decimal.Parse(value2, CultureInfo.InvariantCulture);
                    if (decimalValue1 == decimalValue2)
                    {
                        return 0;
                    }
                    else
                    {
                        if (stringPartType == StringPartType.Asc)
                        {
                            if (decimalValue1 < decimalValue2)
                            {
                                return -1;
                            }
                            else
                            {
                                return 1;
                            }
                        }
                        else
                        {
                            if (decimalValue1 < decimalValue2)
                            {
                                return 1;
                            }
                            else
                            {
                                return -1;
                            }
                        }
                    }
                case "Double":
                    Double doubleValue1 = Double.Parse(value1, CultureInfo.InvariantCulture);
                    Double doubleValue2 = Double.Parse(value2, CultureInfo.InvariantCulture);
                    if (doubleValue1 == doubleValue2)
                    {
                        return 0;
                    }
                    else
                    {
                        if (stringPartType == StringPartType.Asc)
                        {
                            if (doubleValue1 < doubleValue2)
                            {
                                return -1;
                            }
                            else
                            {
                                return 1;
                            }
                        }
                        else
                        {
                            if (doubleValue1 < doubleValue2)
                            {
                                return 1;
                            }
                            else
                            {
                                return -1;
                            }
                        }
                    }
                case "Int16":
                    short shortValue1 = short.Parse(value1, CultureInfo.InvariantCulture);
                    short shortValue2 = short.Parse(value2, CultureInfo.InvariantCulture);
                    if (shortValue1 == shortValue2)
                    {
                        return 0;
                    }
                    else
                    {
                        if (stringPartType == StringPartType.Asc)
                        {
                            if (shortValue1 < shortValue2)
                            {
                                return -1;
                            }
                            else
                            {
                                return 1;
                            }
                        }
                        else
                        {
                            if (shortValue1 < shortValue2)
                            {
                                return 1;
                            }
                            else
                            {
                                return -1;
                            }
                        }
                    }
                case "Int32":
                    int intValue1 = int.Parse(value1, CultureInfo.InvariantCulture);
                    int intValue2 = int.Parse(value2, CultureInfo.InvariantCulture);
                    if (intValue1 == intValue2)
                    {
                        return 0;
                    }
                    else
                    {
                        if (stringPartType == StringPartType.Asc)
                        {
                            if (intValue1 < intValue2)
                            {
                                return -1;
                            }
                            else
                            {
                                return 1;
                            }
                        }
                        else
                        {
                            if (intValue1 < intValue2)
                            {
                                return 1;
                            }
                            else
                            {
                                return -1;
                            }
                        }
                    }
                case "Int64":
                    long longValue1 = long.Parse(value1, CultureInfo.InvariantCulture);
                    long longValue2 = long.Parse(value2, CultureInfo.InvariantCulture);
                    if (longValue1 == longValue2)
                    {
                        return 0;
                    }
                    else
                    {
                        if (stringPartType == StringPartType.Asc)
                        {
                            if (longValue1 < longValue2)
                            {
                                return -1;
                            }
                            else
                            {
                                return 1;
                            }
                        }
                        else
                        {
                            if (longValue1 < longValue2)
                            {
                                return 1;
                            }
                            else
                            {
                                return -1;
                            }
                        }
                    }
                case "String":
                    if (stringPartType == StringPartType.Asc)
                    {
                        return string.Compare(value1, value2, StringComparison.Ordinal);
                    }
                    else
                    {
                        return -1 * string.Compare(value1, value2, StringComparison.Ordinal);
                    }
                default:
                    throw new InvalidOperationException("Unrecognized data type: " + dataTypeName);
            }
        }
    }
}
